/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef DRV_LLD_ARM_GIC_H
#define DRV_LLD_ARM_GIC_H

#include "Std_Types.h"
#include <Compiler.h>
#include "RegHelper.h"
#include "__regs_base.h"
#include "irq_num.h"

#if !defined(ASSEMBLY)

/* MPIDR_EL1, Multiprocessor Affinity Register */
#define MPIDR_AFFLVL_MASK              (0xff)
#define MPIDR_ID_MASK                  (0x00ffffff)

#define MPIDR_AFF0_SHIFT               (0)
#define MPIDR_AFF1_SHIFT               (8)
#define MPIDR_AFF2_SHIFT               (16)

#define MPIDR_AFFLVL(mpidr, aff_level) (((mpidr) >> MPIDR_AFF##aff_level##_SHIFT) & MPIDR_AFFLVL_MASK)

#define MPID_TO_CLUSTER_ID(mpid)       ((mpid) & ~0xff)

/* GIC Distributor register Interface Base Addresses
 * Arm® Generic Interrupt Controller Architecture Specification
 * GIC architecture version 3 and version 4
 */

#define GIC_DIST_BASE                  GIC_BASE
#define GICD_CTLR                      (GIC_DIST_BASE + 0x0)
#define GICD_TYPER                     (GIC_DIST_BASE + 0x4)
#define GICD_IIDR                      (GIC_DIST_BASE + 0x8)
#define GICD_STATUSR                   (GIC_DIST_BASE + 0x10)
#define GICD_SETSPI_NSR                (GIC_DIST_BASE + 0x40)
#define GICD_CLRSPI_NSR                (GIC_DIST_BASE + 0x48)
#define GICD_SETSPI_SR                 (GIC_DIST_BASE + 0x50)
#define GICD_CLRSPI_SR                 (GIC_DIST_BASE + 0x58)
#define GICD_IGROUPRn                  (GIC_DIST_BASE + 0x80)
#define GICD_ISENABLERn                (GIC_DIST_BASE + 0x100)
#define GICD_ICENABLERn                (GIC_DIST_BASE + 0x180)
#define GICD_ISPENDRn                  (GIC_DIST_BASE + 0x200)
#define GICD_ICPENDRn                  (GIC_DIST_BASE + 0x280)
#define GICD_ISACTIVERn                (GIC_DIST_BASE + 0x300)
#define GICD_ICACTIVERn                (GIC_DIST_BASE + 0x380)
#define GICD_IPRIORITYRn               (GIC_DIST_BASE + 0x400)
#define GICD_ITARGETSRn                (GIC_DIST_BASE + 0x800)
#define GICD_ICFGRn                    (GIC_DIST_BASE + 0xc00)
#define GICD_SGIR                      (GIC_DIST_BASE + 0xf00)
#define GICD_IDREGS                    (GIC_DIST_BASE + 0xFFD0)
#define GICD_PIDR2                     (GIC_DIST_BASE + 0xFFE8)

/* Offsets from GICD base or GICR(n) SGI_base */
#define GIC_DIST_IGROUPR               (0x0080)
#define GIC_DIST_ISENABLER             (0x0100)
#define GIC_DIST_ICENABLER             (0x0180)
#define GIC_DIST_ISPENDR               (0x0200)
#define GIC_DIST_ICPENDR               (0x0280)
#define GIC_DIST_ISACTIVER             (0x0300)
#define GIC_DIST_ICACTIVER             (0x0380)
#define GIC_DIST_IPRIORITYR            (0x0400)
#define GIC_DIST_ITARGETSR             (0x0800)
#define GIC_DIST_ICFGR                 (0x0c00)
#define GIC_DIST_IGROUPMODR            (0x0d00)
#define GIC_DIST_SGIR                  (0x0f00)

/* GICD GICR common access macros */
#define IGROUPR(base, n)               (base + GIC_DIST_IGROUPR + (n) * 4)
#define ISENABLER(base, n)             (base + GIC_DIST_ISENABLER + (n) * 4)
#define ICENABLER(base, n)             (base + GIC_DIST_ICENABLER + (n) * 4)
#define ISPENDR(base, n)               (base + GIC_DIST_ISPENDR + (n) * 4)
#define ICPENDR(base, n)               (base + GIC_DIST_ICPENDR + (n) * 4)
#define IPRIORITYR(base, n)            (base + GIC_DIST_IPRIORITYR + n)
#define ITARGETSR(base, n)             (base + GIC_DIST_ITARGETSR + (n) * 4)
#define ICFGR(base, n)                 (base + GIC_DIST_ICFGR + (n) * 4)
#define IGROUPMODR(base, n)            (base + GIC_DIST_IGROUPMODR + (n) * 4)

/* GICD_PIDR2 : Peripheral ID2 Register
 * bit assignments
 * [31:8] - IMPLEMENTATION DEFINED
 * [7:4] ArchRev 0x1. GICv1.
 *               0x2. GICv2.
 *               0x3. GICv3.
 *               0x4. GICv4.
 * [3:0] - IMPLEMENTATION DEFINED.
 */
#define GICD_PIDR2_ARCH_MASK           (0xf0)
#define GICD_PIDR2_ARCH_GICV2          (0x20)
#define GICD_PIDR2_ARCH_GICV3          (0x30)
#define GICD_PIDR2_ARCH_GICV4          (0x40)

/* GICD_TYPER : Interrupt Controller Type Register
 * Arm® Generic Interrupt Controller Architecture Specification
 * GIC architecture version 3 and version 4
 */
#define GICD_TYPER_RSS                 BIT_1(0xFFFFFFFFU, 26)
#define GICD_TYPER_LPIS                BIT_1(0xFFFFFFFFU, 17)
#define GICD_TYPER_MBIS                BIT_1(0xFFFFFFFFU, 16)
#define GICD_TYPER_ESPI                BIT_1(0xFFFFFFFFU, 8)
#define GICD_TYPER_ID_BIT_1(typer)      ((((typer) >> 19) & 0x1f) + 1)
#define GICD_TYPER_NUM_LPIS(typer)     ((((typer) >> 11) & 0x1f) + 1)
#define GICD_TYPER_SPIS(typer)         ((((typer) & 0x1f) + 1) * 32)
#define GICD_TYPER_ESPIS(typer)        (((typer) & GICD_TYPER_ESPI) ? GICD_TYPER_SPIS((typer) >> 27) : 0)

/* Common Helper Constants */
#define GIC_SGI_INT_BASE               (0)
#define GIC_PPI_INT_BASE               (16)
#define GIC_IS_SGI(intid)              (((intid) >= GIC_SGI_INT_BASE) && ((intid) < GIC_PPI_INT_BASE))

#define GIC_SPI_INT_BASE               (32)
#define GIC_INTR_MAX_NUM              (IRQ_MAX_INTR_NUM)
#define GIC_IS_SPI(intid)              (((intid) >= GIC_SPI_INT_BASE) && ((intid) <= GIC_INTR_MAX_NUM))
#define GIC_NUM_INTR_PER_REG           (32)
#define GIC_NUM_CFG_PER_REG            (16)
#define GIC_NUM_PRI_PER_REG            (4)

/* GIC idle priority : value '0xff' will allow all interrupts */
#define GIC_IDLE_PRIO                  (0xff)
/* Default priority */
#define DEFAULT_IRQ_PRIORITY           (14)
/* Priority levels 0:255 */
#define GIC_PRI_MASK                   (0xff)
/* supported max priority. */
#define GIC_PRI_MAX                    (31)

/* '0xa0'is used to initialize each interrtupt default priority.
 * This is an arbitrary value in current context.
 * Any value '0x80' to '0xff' will work for both NS and S state.
 * The values of individual interrupt and default has to be chosen
 * carefully if PMR and BPR based nesting and preemption has to be done.
 */
#define GIC_INT_DEF_PRI_X4             (0xa0a0a0a0)

/* GICD_CTLR : Distributor Control Register
 *
 * [31](RO)  RWP Register Write Pending:
 *            -- 0 No register write in progress
 *            -- 1 Register write in progress
 * [30:8]    - Reserved -
 * [7](RO)   E1NWF Enable 1 of N Wakeup Functionality, always zero
 * [6](RO)   DS Disable Security status, always one
 * [5]       - Reserved -
 * [4](RO)   ARE Affinity Routing Enable, always one
 * [3:2]     - Reserved -
 * [1](RW)   EnableGrp1 Enable Group 1 interrupts
 * [0](RW)   EnableGrp0 Enable Group 0 interrupts
 */
#define GICD_CTLR_ENABLE_G0            (0U)
#define GICD_CTLR_ENABLE_G1            (1U)
#define GICD_CTRL_ARE                  (4U)
#define GICD_CTRL_DS                   (6U)
#define GICD_CTRL_E1NWF                (7U)

/* GICD_CTLR Register write progress bit */
#define GICD_CTLR_RWP                  (31)

/* GICD_IPRIORITYR */
#define GICD_IPRIORITY_SHIFT            (3U)

/* GICR_CTLR */
#define GICR_CTLR_ENABLE_LPIS          BIT_1(0xFFFFFFFFU, 0)
#define GICR_CTLR_RWP                  (3)

/* GICD_TYPER.ITLinesNumber 0:4 */
#define GICD_TYPER_ITLINESNUM_MASK     (0x1f)

/* Core0 Re-Distributor registers base */
#define GIC_RDIST_BASE                 (GIC_BASE + 0x100000)
#define GIC_RDIST_OFFSET               (0x20000)

/* GICR： Re-Distributor registers, offsets from RD_base(n) */
#define GICR_CTLR                      (0x0000)
#define GICR_IIDR                      (0x0004)
#define GICR_TYPER                     (0x0008)
#define GICR_STATUSR                   (0x0010)
#define GICR_WAKER                     (0x0014)
#define GICR_PWRR                      (0x0024)
#define GICR_SETLPIR                   (0x0040)
#define GICR_CLRLPIR                   (0x0048)
#define GICR_PROPBASER                 (0x0070)
#define GICR_PENDBASER                 (0x0078)
#define GICR_INVLPIR                   (0x00A0)
#define GICR_INVALLR                   (0x00B0)
#define GICR_SYNCR                     (0x00C0)
#define GICR_MOVLPIR                   (0x0100)
#define GICR_MOVALLR                   (0x0110)
#define GICR_IDREGS                    (0xFFD0)
#define GICR_PIDR2                     (0xFFE8)

/* GICR_PIDR2 : Peripheral ID2 Register
 * bit assignments are the same as those for GICD_PIDR2)
 * [31:8] - IMPLEMENTATION DEFINED
 * [7:4] ArchRev 0x1. GICv1.
 *               0x2. GICv2.
 *               0x3. GICv3.
 *               0x4. GICv4.
 * [3:0] - IMPLEMENTATION DEFINED.
 */
#define GICR_PIDR2_ARCH_MASK           (0xf0)
#define GICR_PIDR2_ARCH_GICV3          (0x30)
#define GICR_PIDR2_ARCH_GICV4          (0x40)

/* GICR_TYPER : Redistributor Type Register
 * Arm® Generic Interrupt Controller Architecture Specification
 * GIC architecture version 3 and version 4
 * chapter 9.11.35 for detail descriptions
 */
#define GICR_TYPER_PLPIS               BIT_1(0xFFFFFFFFU, 0)
#define GICR_TYPER_VLPIS               BIT_1(0xFFFFFFFFU, 1)
#define GICR_TYPER_DIRECTLPIS          BIT_1(0xFFFFFFFFU, 3)
#define GICR_TYPER_LAST                BIT_1(0xFFFFFFFFU, 4)

/* GICR_WAKER */
#define GICR_WAKER_PS                  (1)
#define GICR_WAKER_CA                  (2)

/* ICC_PMR */
#define ICC_PMR_PRI_SHIFT              (3U)

/* SGI base is at 64K offset from Redistributor */
#define GICR_SGI_BASE_OFF              (0x10000)

/* GICD_ICFGR */
#define GICD_ICFGR_MASK                BIT_MASK(2)
#define GICD_ICFGR_TYPE                BIT_1(0xFFFFFFFFU, 1)

/* BIT_1(0) reserved for IRQ_ZERO_LATENCY */
#define IRQ_TYPE_LEVEL                 BIT_1(0xFFFFFFFFU, 1)
#define IRQ_TYPE_EDGE                  BIT_1(0xFFFFFFFFU, 2)

/* GITCD_IROUTER */
#define GIC_DIST_IROUTER               (0x6000)
#define IROUTER(base, n)               (base + GIC_DIST_IROUTER + (n) * 8)

/* trigger mode. BIT_1(0) reserved for IRQ_ZERO_LATENCY */
#define IRQ_TYPE_LEVEL                 BIT_1(0xFFFFFFFFU, 1)
#define IRQ_TYPE_EDGE                  BIT_1(0xFFFFFFFFU, 2)


/* group0 or group1 interrupt */
#define IRQ_GROUP0                     (1U << 0U)
#define IRQ_GROUP1                     (1U << 1U)
#define IRQ_SPI_GROUP0                 (1U << 2U)
#define IRQ_SPI_GROUP1                 (1U << 3U)

/* [7:3] all for group-priority defaut */
#define IRQ_PRI_5_BITS_GROUP           (1U)
/* [7:4] for group-priority, [3] for sub-priority */
#define IRQ_PRI_4_BITS_GROUP           (2U)
/* [7:5] for group-priority, [4:3] for sub-priority */
#define IRQ_PRI_3_BITS_GROUP           (3U)
/* [7:6] for group-priority, [4:3] for sub-priority */
#define IRQ_PRI_2_BITS_GROUP           (4U)
/* [7] for group-priority, [6:3] for sub-priority */
#define IRQ_PRI_1_BITS_GROUP           (5U)
/* [7:3] for sub-priority */
#define IRQ_PRI_0_BITS_GROUP           (6U)

/* interrupt group priority */
#define IRQ_PRI_GROUP5(gpri)           (gpri)
#define IRQ_PRI_GROUP4(gpri, sub_pri)  ((((gpri) << 1U) & 0xfU) | ((sub_pri) & 0x1U))
#define IRQ_PRI_GROUP3(gpri, sub_pri)  ((((gpri) << 2U) & 0x7U) | ((sub_pri) & 0x3U))
#define IRQ_PRI_GROUP2(gpri, sub_pri)  ((((gpri) << 3U) & 0x3U) | ((sub_pri) & 0x7U))
#define IRQ_PRI_GROUP1(gpri, sub_pri)  ((((gpri) << 4U) & 0x1U) | ((sub_pri) & 0xfU))
#define IRQ_PRI_GROUP0(sub_pri)        (sub_pri)


#define GIC_IRQ_SGI0                   (0)
#define GIC_IRQ_SGI1                   (1)
#define GIC_IRQ_SGI2                   (2)
#define GIC_IRQ_SGI3                   (3)
#define GIC_IRQ_SGI4                   (4)
#define GIC_IRQ_SGI5                   (5)
#define GIC_IRQ_SGI6                   (6)
#define GIC_IRQ_SGI7                   (7)
#define GIC_IRQ_SGI8                   (8)
#define GIC_IRQ_SGI9                   (9)
#define GIC_IRQ_SGI10                  (10)
#define GIC_IRQ_SGI11                  (11)
#define GIC_IRQ_SGI12                  (12)
#define GIC_IRQ_SGI13                  (13)
#define GIC_IRQ_SGI14                  (14)
#define GIC_IRQ_SGI15                  (15)

/* register constants */
#define ICC_SRE_ELX_SRE_BIT            BIT_1(0xFFFFFFFFU, 0)
#define ICC_SRE_ELX_DFB_BIT            BIT_1(0xFFFFFFFFU, 1)
#define ICC_SRE_ELX_DIB_BIT            BIT_1(0xFFFFFFFFU, 2)
#define ICC_SRE_EL3_EN_BIT             BIT_1(0xFFFFFFFFU, 3)

/* ICC SGI macros */
#define SGIR_TGT_MASK                  (0xffff)
#define SGIR_AFF1_SHIFT                (16)
#define SGIR_AFF2_SHIFT                (32)
#define SGIR_AFF3_SHIFT                (48)
#define SGIR_AFF_MASK                  (0xf)
#define SGIR_INTID_SHIFT               (24)
#define SGIR_INTID_MASK                (0xf)
#define SGIR_IRM_SHIFT                 (40)
#define SGIR_IRM_MASK                  (0x1)
#define SGIR_IRM_TO_AFF                (0)
#define SGIR_IRM_TO_ALL                (1)

#define GICV3_SGIR_VALUE(_aff3, _aff2, _aff1, _intid, _irm, _tgt)                                                      \
    ((((uint64)(_aff3) & SGIR_AFF_MASK) << SGIR_AFF3_SHIFT) |                                                        \
     (((uint64)(_irm) & SGIR_IRM_MASK) << SGIR_IRM_SHIFT) |                                                          \
     (((uint64)(_aff2) & SGIR_AFF_MASK) << SGIR_AFF2_SHIFT) | (((_intid) & SGIR_INTID_MASK) << SGIR_INTID_SHIFT) |   \
     (((_aff1) & SGIR_AFF_MASK) << SGIR_AFF1_SHIFT) | ((_tgt) & SGIR_TGT_MASK))

/* selects redistributor SGI_base for current core for PPI and SGI                                              \
 * selects distributor base for SPI                                                                             \
 * The macro translates to distributor base for GICv2 and GICv1                                                 \
 */
#define GET_DIST_BASE(intid) ((intid < GIC_SPI_INT_BASE) ? (gic_get_rdist() + GICR_SGI_BASE_OFF) : GIC_DIST_BASE)
#define GICC_IAR_INT_ID_MASK (0x3ff)
#define IGROUPR_VAL          (0xFFFFFFFFU)

/* rwp mask read timeout */
#define GIC_RWP_TIMEOUT 400000U
/* wakeup timeout */
#define GIC_WAKER_TIMEOUT 400000U


/**
 * @brief vic return status.
 */
enum sdrv_gic_status
{
    /**< status for success. */
    SDRV_GIC_STATUS_SUCCESS = E_OK,
    /**< status for fail. */
    SDRV_GIC_STATUS_FAIL = E_NOT_OK,
    /**< status for busy. */
    SDRV_GIC_STATUS_BUSY = E_NOT_OK,
    /**< status for invalid param. */
    SDRV_GIC_STATUS_INVALID_PARAM = E_NOT_OK,
    /**< invalid interrupt */
    SDRV_GIC_STATUS_INVALID_IRQ = E_NOT_OK,
};

/**
 * @brief initialize gic
 *
 * initialize the gic distributor, redistributor
 * and cpu interface.
 * The API should be called by the main core.
 *
 * @return status code of operation.
 */
int arm_gic_init(void);

/**
 * @brief gic support max interrupt id
 */
uint32 arm_gic_suppot_max_intid(void);


/**
 * @brief initialize gic for secondary core
 *
 * initialize redistributor and cpu interface.
 * The API should be called by the other cores except main core.
 *
 * @return status code of operation.
 */
int arm_gic_secondary_init(void);

/**
 * @brief set interrupt priority mask
 *
 * @param[in] mask the set priority.
 * @return previous priority.
 */
uint32 arm_gic_irq_mask(uint32 mask);

/**
 * @brief umask all interrupt priority
 */
void arm_gic_irq_unmask(void);

/**
 * @brief enable interrupt
 *
 * @param[in] intid the interrupt id.
 */
void arm_gic_irq_enable(uint32 intid);

/**
 * @brief disable interrupt
 *
 * @param[in] intid the interrupt id.
 * @return status code of operation.
 */
int arm_gic_irq_disable(uint32 intid);

/**
 * @brief set interrupt type
 *
 * @param[in] intid the interrupt id.
 * @param[in] irq_type the interrupt type.
 */
void arm_gic_set_irq_type(uint32 intid, uint8 irq_type);

/**
 * @brief set interrupt priority
 *
 * @param[in] intid the interrupt id.
 * @param[in] priority the interrupt priority.
 */
void arm_gic_irq_set_priority(uint32 intid, uint32 priority);

/**
 * @brief get interrupt priority
 *
 * @param[in] intid the interrupt id.
 * @return the interrupt priority.
 */
int arm_gic_irq_get_priority(uint32 intid);

/**
 * @brief get current active interrupt priority
 *
 * @return the interrupt priority.
 */
uint32 arm_gic_irq_get_current_priority(void);

/**
 * @brief get active interrupt id of group1
 *
 * @return the interrupt id.
 */
uint32 arm_gic_irq_ack(void);

/**
 * @brief end of interrupt for group1
 *
 * @param[in] intid the interrupt id.
 */
void arm_gic_irq_eoi(uint32 intid);

/**
 * @brief set interrupt route
 *
 * @param[in] intid the interrupt id.
 * @param[in] target the core id of target.
 * @return status code of operation.
 */
int arm_gic_set_affinity(uint32 target, uint32 intid);

/**
 * @brief raise sgi
 *
 * @param[in] sgi_id the sgi interrupt id.
 * @param[in] target_list the bitmask of multi-target.
 * @return status code of operation.
 */
int arm_gic_raise_sgi(uint32 sgi_id, uint16 target_list);

/**
 * @brief set interrupt as group0 or group1
 *
 * @param[in] intid the interrupt id.
 * @param[in] group group0 or group1.
 * @return status code of operation.
 */
int arm_gic_set_group(uint32 intid, uint16 group);

/**
 * @brief enable group0 or group1 interrupt
 *
 * @param[in] group group0 or group1.
 * @return status code of operation.
 */
int arm_gic_enable_group_interrupt(uint16 group);

/**
 * @brief disable group0 or group1 interrupt
 *
 * @param[in] group group0 or group1.
 * @return status code of operation.
 */
int arm_gic_disable_group_interrupt(uint16 group);

/**
 * @brief set interrupt priority group
 *
 * The priority is divided into group-priority and sub-priority,
 * and group-priority can be used for preemption.
 *
 * @param[in] group group0 or group1.
 * @param[in] pri priority split point.
 * @return status code of operation.
 */
int arm_gic_set_priority_group(uint16 group, uint16 pri);

/**
 * @brief raise spi
 *
 * @param[in] spi_id spi interrupt id.
 * @return status code of operation.
 */
int arm_gic_raise_spi(uint32 spi_id);

/**
 * @brief check irq log status
 *
 * check irq log status for spi interrupt.
 *
 * @param[in] spi_id spi interrupt id.
 * @return the interrupt if valid or not.
 */
int irq_log_check_status(uint32 spi_id);

#endif

#endif
